/**
    @file text_overlay.cpp
    @brief Text output onto the HUD
*/
#include "hud/text_overlay.hpp"

// Don't know, what this member does
template<> pm3::hud::TEXT_OVERLAY* Ogre::Singleton<pm3::hud::TEXT_OVERLAY>::ms_Singleton = 0;

pm3::hud::TEXT_OVERLAY::TEXT_OVERLAY() {
    m_visible = true;

    m_overlay_manager = NULL;
    m_overlay = NULL;
    m_panel = NULL;

    // Create the text overlay
    create();
}

pm3::hud::TEXT_OVERLAY::~TEXT_OVERLAY() {
}

void pm3::hud::TEXT_OVERLAY::create() {
   // Destroy the previous - if any
   destroy();

   try {
        // Get the overlay manager pointer
        m_overlay_manager = Ogre::OverlayManager::getSingletonPtr();
        // Create the text overlay
        m_overlay = m_overlay_manager->create( "TEXT_OVERLAY" );
        // Create an overlay container
        m_panel = static_cast<Ogre::OverlayContainer*>( m_overlay_manager->createOverlayElement( "Panel", "Container1" ) );
        // It should cover the whole screen
        m_panel->setDimensions( 1, 1 );
        m_panel->setPosition( 0, 0 );
        // Add the panel
        m_overlay->add2D( m_panel );

        // Show the panel
        show();
    } catch (...) {
        // Just fall through quietly
    }
}

void pm3::hud::TEXT_OVERLAY::destroy() {
    try {
        if (m_overlay) {
            if (m_overlay_manager)
                m_overlay_manager->destroy( "TEXT_OVERLAY" );

            delete m_overlay;
        }
        if (m_panel)
            delete m_panel;
    } catch (...) {
        // Just fall through quietly
    }

    m_overlay = NULL;
    m_panel = NULL;
}

void pm3::hud::TEXT_OVERLAY::show() {
    m_visible = true;

    if (m_overlay)
        m_overlay->show();
}

void pm3::hud::TEXT_OVERLAY::hide() {
    m_visible = false;

    if (m_overlay)
        m_overlay->hide();
}

bool pm3::hud::TEXT_OVERLAY::is_valid() {
    return (m_overlay_manager && m_overlay && m_panel);
}


void pm3::hud::TEXT_OVERLAY::add_textbox( const std::string &r_name, const std::string &r_text, 
                                        Ogre::Real f_x, Ogre::Real f_y, Ogre::Real f_width, Ogre::Real f_height,
                                        const Ogre::ColourValue &r_color )
{
    if (is_valid()) {
        // Create an overlay element
        Ogre::OverlayElement *text_area = m_overlay_manager->createOverlayElement( "TextArea", r_name );
        // Set metrics in pixels
        text_area->setMetricsMode( Ogre::GMM_PIXELS );
        // Set its position,
        text_area->setPosition( f_x, f_y );
        // dimensions
        text_area->setDimensions( f_width, f_height );
        // and a few other parameters
        text_area->setParameter( "font_name", "bluehigh" );
        text_area->setParameter( "char_height", "14" );
        // Set textbox color
        text_area->setColour( r_color );
        // and its caption
        text_area->setCaption( r_text );
        // Add the TextArea
        m_panel->addChild( text_area );
    }
}

void pm3::hud::TEXT_OVERLAY::remove_textbox( const std::string &r_name ) {
    if (is_valid()) {
        // Remove the textbox from the panel
        m_panel->removeChild( r_name );
        // And destroy the overlay element
        m_overlay_manager->destroyOverlayElement( r_name );
    }
}

void pm3::hud::TEXT_OVERLAY::set_text( const std::string &r_name, const std::string &r_text ) {
    if (is_valid()) {
        // Get a pointer to the overlay element
        Ogre::OverlayElement *text_area = m_overlay_manager->getOverlayElement( r_name );
        // And set its caption
        text_area->setCaption( r_text );
    }
}

void pm3::hud::TEXT_OVERLAY::printf( const std::string &r_name, const char *p_fmt, ... ) {
    va_list     ap;
    char        *buf = NULL;
    char        *newbuf = NULL;
    int         buffered = 0;
    int         size = m_format_buffer_size;

    // Nothing to be written?
    if (p_fmt == NULL)
        return;

    // Is the log okay?
    if (!is_valid())
        return;

    // Allocate memory for the output buffer
    buf = (char *) malloc( size * sizeof( char ) );

    if (!buf)
        return;

    // Basically a safe vsprintf
    while (true) {
        // Try to print in the allocated space.
        va_start( ap, p_fmt );

        buffered = vsnprintf( buf, size, p_fmt, ap );

        va_end( ap );

        // If that worked, output the string.
        if (buffered > -1 && buffered < size)
            break;
		// Else try again with more space.
        if (buffered > -1)          // glibc 2.1
            size = buffered + 1;    // precisely what is needed
        else                        // glibc 2.0
            size *= 2;              // twice the old size
        // Reallocate for more space
		if (( newbuf = (char *) realloc( buf, size ) ) == NULL) {
			free( buf );
			return;
		} else
			buf = newbuf;
	}

	// Output the text
    set_text( r_name, std::string( buf ) );

	// Free the buffer
	free( buf );
}

